/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.core.impl;

import android.content.Context;
import android.support.annotation.NonNull;

import com.xuexiang.xdlink.component.command.IProtocolCommand;
import com.xuexiang.xdlink.component.command.impl.ProtocolCommand;
import com.xuexiang.xdlink.component.message.IMessage;
import com.xuexiang.xdlink.component.message.IRequestMessage;
import com.xuexiang.xdlink.component.protocol.OnReceivedMessageListener;
import com.xuexiang.xdlink.core.IDeviceConnectCenter;
import com.xuexiang.xdlink.core.IDeviceDataProvider;
import com.xuexiang.xdlink.core.OnDeviceConnectListener;
import com.xuexiang.xdlink.logs.DCFLog;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 抽象的设备数据提供者
 *
 * @author xuexiang
 * @since 2018/8/30 上午1:25
 */
public abstract class AbstractDeviceDataProvider implements IDeviceDataProvider, OnDeviceConnectListener, OnReceivedMessageListener {

    private Context mContext;
    /**
     * 设备连接中心
     */
    private IDeviceConnectCenter mIDeviceConnectCenter;

    /**
     * 同步锁，防止设备多次被调用导致出错[连接、断开、写入等都要加同步锁]
     */
    private final Lock mLock = new ReentrantLock();

    /**
     * 构造函数
     *
     * @param context
     */
    public AbstractDeviceDataProvider(Context context) {
        mContext = context.getApplicationContext();
    }

    /**
     * 初始化设备，构建设备连接中心【必须被调用】
     */
    @Override
    public IDeviceDataProvider initDevice() {
        mIDeviceConnectCenter = buildDeviceConnectCenter(mContext);
        return this;
    }

    @Override
    public void connectDevice() {
        if (mIDeviceConnectCenter != null) {
            mLock.lock();
            try {
                if (mIDeviceConnectCenter != null) {
                    DCFLog.i("[DeviceDataProvider] is start to connect device...");
                    mIDeviceConnectCenter.connectDevice();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
    }

    @Override
    public void disConnectDevice() {
        if (mIDeviceConnectCenter != null) {
            mLock.lock();
            try {
                if (mIDeviceConnectCenter != null) {
                    DCFLog.i("[DeviceDataProvider] is start to disconnect device...");
                    mIDeviceConnectCenter.disConnectDevice();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
    }

    @Override
    public void onReceivedMessage(IMessage message) {

    }

    @Override
    public void onDeviceTimeout(IDeviceConnectCenter deviceConnectCenter) {

    }

    @Override
    public boolean sendMessage(@NonNull IRequestMessage message) {
        if (mIDeviceConnectCenter != null) {
            mLock.lock();
            try {
                if (mIDeviceConnectCenter != null) {
                    DCFLog.d("[DeviceDataProvider] send request message to channel, message:" + message.getClass().getSimpleName());
                    return mIDeviceConnectCenter.sendMessage(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
        return false;
    }

    @Override
    public boolean executeCommand(int commandType) {
        return executeCommand(new ProtocolCommand<>(commandType));
    }

    @Override
    public <T> boolean executeCommand(int commandType, T content) {
        return executeCommand(new ProtocolCommand<>(commandType, content));
    }

    @Override
    public boolean executeCommand(IProtocolCommand command) {
        if (mIDeviceConnectCenter != null) {
            mLock.lock();
            try {
                if (mIDeviceConnectCenter != null) {
                    DCFLog.d("[DeviceDataProvider] start to execute protocol command, command:" + command.toString());
                    mIDeviceConnectCenter.executeCommand(command);
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
        return false;
    }

    @Override
    public boolean isChannelOpen() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter.isChannelOpen();
    }

    @Override
    public void reOpenChannel() {
        if (mIDeviceConnectCenter != null) {
            mIDeviceConnectCenter.reOpenChannel();
        }
    }

    @Override
    public int getReOpenCount() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter.getReOpenCount();
    }

    @Override
    public void startMonitor() {
        if (mIDeviceConnectCenter != null) {
            mIDeviceConnectCenter.startMonitor();
        }
    }

    @Override
    public void stopMonitor() {
        if (mIDeviceConnectCenter != null) {
            mIDeviceConnectCenter.stopMonitor();
        }
    }

    @Override
    public boolean isMonitorRunning() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter.isMonitorRunning();
    }

    @Override
    public void switchDataSaveStatus(boolean isTurnOn) {
        if (mIDeviceConnectCenter != null) {
            mIDeviceConnectCenter.switchDataSaveStatus(isTurnOn);
        }
    }

    @Override
    public boolean deleteOriginalData() {
        return mIDeviceConnectCenter != null && mIDeviceConnectCenter.cancelDataSave();
    }

    @Override
    public String getDeviceType() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter.getDeviceType();
    }

    @Override
    public boolean hasReceivedData() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter.hasReceivedData();
    }

    private void testDeviceCenterInitialization() {
        if (mIDeviceConnectCenter == null) {
            throw new NullPointerException("mIDeviceConnectCenter can never be null! Please call the 'initDevice' Method to initialize it.");
        }
    }

    public Context getContext() {
        return mContext;
    }

    /**
     * 获取字符串
     *
     * @param resId
     * @return
     */
    public String getString(int resId) {
        return mContext.getResources().getString(resId);
    }

    /**
     * @return 获取设备连接中心
     */
    public IDeviceConnectCenter getIDeviceConnectCenter() {
        testDeviceCenterInitialization();
        return mIDeviceConnectCenter;
    }
}
